// Définition des tokens utilisés
%token ID CSTE AFF RELOP CLASS EXTENDS VAL VAR NW DEF RETURNS IS OVERRIDE IF THEN ELSE SELF SUPER STRING

// Définition des précédences des opérateurs
%right AFF
%nonassoc RELOP
%left '+' '-'
%left '*'
%left '/'
%left '.'

/* Les indications ci-dessous servent a indiquer a Bison que les "valeurs" $i
 * ou $$ associées a ces non-terminaux doivent utiliser la variante indiquee
 * de l'union YYSTYPE.
 * La "valeur" associée a un terminal doit toujours utiliser la variante de
 * meme type
 */
%type <C> Aff RelOp
%type <T> OptExt
%type <L> LDec
%type <V> LDeclChamp DeclChamp OptArg LArgs
%type <X> LExpr Params LParams ExprOpt
%type <B> Bloc
%type <M> LDeclMeth DeclMeth
%type <A> Ident CsteLit Expr Selection Cible Envoi
%type <E> OverrideOpt
%type <S> Nom


%{
#include <stdio.h>	/* needed for compiling the file generated by Bison */
#include "main.h"	/* definition des types et des etiquettes utilises */

extern int yylex();
extern void yyerror();

/* Les fonctions ci-dessous sont definies dans main.c */
extern pclasse AddClassName(), GetClasseIn(), NewInstanceOf(), initClasses();
extern pext MakeEXTENDS();
extern pmeth MakeMeth(), MakeLMeth();
extern pfct MakeFct();

/* Constructeurs d'arbres de syntaxe abstraite */
extern pvar MakeVar(), GetVarIn(), CopyLVar(), MakeLVar();
extern parbre MakeId(), MakeCsteInt(), MakeCsteString(), MakeNoeud(), MakeITE();
extern parbre MakeUnary(), MakeIdChaine(), MakeNEW(), MakeBloc();
extern parbre MakeEnvoi(), MakeSelec(), MakeCible();

extern pbloc CreateBloc();
extern pexpr MakeExpr(), MakeLExpr();

/* Fonction d'evaluation de l'interprete */
extern int evalue(), lire();

/* Fonction d'impression de la liste des variables declarees */
extern void imprime(), FillClass(), executeMain();

/* Variable globale nécessaire pour le contexte global des classes */
extern pclasse globalClasses;


%}


%%
/* Principe de construction de l'interprete:
 *
 * On reconnnait une liste (eventuellement vide) de declarations de variables
 * avec leur initialisation, puis le programme principal.
 * Au fur et a mesure qu'on reconnait des declarations de variables, on
 * construit une liste chainee avec les noms de variables et leur valeur.
 * Pendant l'analyse d'une declaration x := expr  on construit un arbre de
 * syntaxe abstraite qui represente l'expression. Pendant la construction
 * de l'arbre, on verifie a chaque occurrence d'un identificateur que cet
 * identificateur apparait bien dans la liste des variables deja declarees.
 * Une fois que l'arbre representant 'expr' est construit, on le passe en
 * parametre, avec la liste des variables deja declarees, a une fonction
 * d'evaluation qui va appliquer les opérateurs, en cherchant les valeurs
 * des variables dans la liste. Quand la valeur de l'expression est retournee
 * on ajoute un nouvel element a la liste des variables...
 *
 * Les macros d'allocation NEW et de nullite (nil) sont definies dans main.h.
 * Leur usage n'est bien sur pas obligatoire.
 * Voir la definition des types classe, pclasse, var, pvar, arbre, parbre dans main.h
 */


S : LDec Bloc	{ executeMain($1, $2); }
;

LDec : CLASS Nom OptArg OptExt '{' LDeclChamp LDeclMeth '}'			{ pclasse curClass = AddClassName($2, globalClasses);
																	  FillClass(curClass, $6, $7, $3, $4);
																	  globalClasses = curClass;
																	  $$ = globalClasses; }
	 | LDec CLASS Nom OptArg OptExt '{' LDeclChamp LDeclMeth '}'	{ pclasse curClass = AddClassName($3, $1);
																	  FillClass(curClass, $7, $8, $4, $5);
																	  globalClasses = curClass;
																	  $$ = globalClasses; }
;

OptArg : 				{ $$ = nil(var); }
	   | '(' LArgs ')'	{ $$ = $2; }
;

LArgs : Nom ':' Nom				{ $$ = MakeLVar(MakeVar($1, FALSE, GetClasseIn($3, globalClasses), TRUE, nil(expr)), nil(var)); }
	  | LArgs ',' Nom ':' Nom	{ $$ = MakeLVar(MakeVar($3, FALSE, GetClasseIn($5, globalClasses), TRUE, nil(expr)), $1); }
;

OptExt : 					{ $$ = nil(ext); }
	   | EXTENDS Nom Params	{ $$ = MakeEXTENDS(GetClasseIn($2, globalClasses), $3); }
;

Params : 					{ $$ = nil(expr); }
	   | '(' LParams ')'	{ $$ = $2; }
;

LParams : Expr				{ $$ = MakeLExpr($1, nil(expr)); }
		| LParams ',' Expr	{ $$ = MakeLExpr($3, $1); }
;

Nom : ID	{ $$ = yylval.S; }
;

Ident : Nom		{ $$ = MakeId($1); }
	  | SELF	{ $$ = MakeNoeud(Self, nil(arbre), nil(arbre)); }
	  | SUPER	{ $$ = MakeNoeud(Super, nil(arbre), nil(arbre)); }
	  /* Peut-être trouver un moyen plus "sexy" de représenter Self et Super. */
;

LDeclChamp : DeclChamp					{ $$ = MakeLVar($1, nil(var)); }
		   | LDeclChamp ';' DeclChamp	{ $$ = MakeLVar($3, $1); }
;

DeclChamp : VAL OverrideOpt Nom ':' Nom AFF Expr	{ $$ = MakeVar($3, $2, GetClasseIn($5, globalClasses), FALSE, $7); }
		  | VAR Nom ':' Nom ExprOpt					{ $$ = MakeVar($2, FALSE, GetClasseIn($4, globalClasses), TRUE, $5); }
;

ExprOpt : 			{ $$ = nil(expr); }
		| AFF Expr	{ $$ = MakeExpr($2); }
;

LDeclMeth : DeclMeth			{ $$ = MakeLMeth($1, nil(meth)); }
		  | LDeclMeth DeclMeth	{ $$ = MakeLMeth($1, $2); }
;

DeclMeth : DEF OverrideOpt Nom '(' LArgs ')' IS Bloc		{ $$ = MakeMeth($3, $2, TRUE, $5, $8, nil(classe)); }
		 | DEF OverrideOpt Nom '(' ')' IS Bloc				{ $$ = MakeMeth($3, $2, TRUE, nil(var), $7, nil(classe)); }
		 | DEF OverrideOpt Nom OptArg RETURNS Nom IS Bloc	{ if($4) $$ = MakeMeth($3, $2, TRUE, $4, $8, GetClasseIn($6, globalClasses));
															  else $$ = MakeMeth($3, $2, FALSE, $4, $8, GetClasseIn($6, globalClasses)); }
		 | DEF OverrideOpt Nom '(' ')' RETURNS Nom IS Bloc	{ $$ = MakeMeth($3, $2, TRUE, nil(var), $9, GetClasseIn($7, globalClasses)); }
;

OverrideOpt : 			{ $$ = FALSE; }
			| OVERRIDE	{ $$ = TRUE; }
;

Bloc : '{' LExpr '}'				{ $$ = CreateBloc($2, nil(var)); }
	 | '{' LDeclChamp IS LExpr '}'	{ $$ = CreateBloc($4, $2); }
;

LExpr : Expr			{ $$ = MakeLExpr($1, nil(expr)); }
	  | LExpr ';' Expr	{ $$ = MakeLExpr($3, $1); }
;

Expr : Bloc							{ $$ = MakeBloc($1); }
	 | Cible Aff Expr				{ $$ = MakeNoeud($2, $1, $3); }
	 | IF Expr THEN Expr ELSE Expr	{ $$ = MakeITE($2, $4, $6); }
	 | Ident						{ $$ = $1; }
	 | Selection					{ $$ = $1; } //Selection et envoi de message
	 | Envoi						{ $$ = $1; }
	 | CsteLit						{ $$ = $1; } //Le noeud CSTE est déjà créé dans la règle CsteLit
	 | '(' Expr ')'					{ $$ = $2; }
	 | NW Nom Params				{ $$ = MakeNEW(GetClasseIn($2, globalClasses), $3); }
	 | Expr RelOp Expr				{ $$ = MakeNoeud($2, $1, $3); }
	 | Expr '+' Expr				{ $$ = MakeNoeud('+', $1, $3); }
	 | Expr '-' Expr				{ $$ = MakeNoeud('-', $1, $3); }
	 | Expr '/' Expr				{ $$ = MakeNoeud('/', $1, $3); }
	 | Expr '*' Expr				{ $$ = MakeNoeud('*', $1, $3); }
	 | '+' Expr						{ $$ = MakeUnary('+', $2); }
	 | '-' Expr						{ $$ = MakeUnary('-', $2); }
;

Aff : AFF { $$ = yylval.C; }
;

RelOp : RELOP	{ $$ = yylval.C; }
;

Cible : Nom			{ $$ = MakeCible(MakeId($1)); }
	  | Selection	{ $$ = MakeCible($1); }
	/* La cible est forcément une
	 * instance donc fini forcément par un Nom. A vérifier si c'est
	 * bien une variable (et non une méthode sans paramètre). */
;

/* On va confondre Selection et envoi de message ici, ce sera plus simple.
 * Dans la mesure où sélection de méthode et envoi de message ne diffèrent que
 * par le fait que l'un a des arguments et pas l'autre, on peut se permettre
 * de les mettre dans le même panier. Pour se simplifier la vie, on ne va pas
 * gérer l'appel immédiat de méthode, l'utilisateur devra alors faire précéder
 * l'appel de la méthode par self. (ou super.). */
Selection : Expr '.' Nom	{ $$ = MakeSelec($1, MakeId($3)); }
;

Envoi : Expr '.' Nom '(' LParams ')'	{ $$ = MakeEnvoi($1, MakeFct(MakeId($3), $5)); }
	  | Expr '.' Nom '(' ')'			{ $$ = MakeEnvoi($1, MakeFct(MakeId($3), nil(expr))); }
;

CsteLit : CSTE		{ $$ = MakeCsteInt(yylval.E); }
		| STRING	{ $$ = MakeCsteString(yylval.S); }
;
